package Graphing;

import P3.AddressConverter;
import P3.CrawlResult;
import java.awt.geom.Point2D;
import java.sql.Savepoint;
import java.util.Iterator;
import java.util.Vector;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;

public class GnutellaGraph extends Graph
{
	private Vector<String> mFiles;

	public GnutellaGraph(int width, int height)
	{
		super(width, height);
		mFiles = new Vector<String>();
	}
        
        public int CountStatus(String status)
        {
            int count = 0;
            Iterator<GraphNode> nodeIt = mNodes.iterator();
            while(nodeIt.hasNext())
            {
    		GnutellaNode node = (GnutellaNode)nodeIt.next();
    		CrawlResult result = node.GetLatestResult();
                if(result != null && result.Status.equals(status))
                {
                    ++count;
                }
            }
            
            return count;
        }
	
    public void GenerateHTML(String directory, int maxWorkers, int timeTaken)
    {
        System.out.println("Generate HTML");
    	File root = new File(directory);
    	File html = new File(root, "index_temp.htm"); // renamed to index.htm at end of function to avoid trying to load the page mid-update
    	FileWriter htmlWriter;

		try {
			if(!root.exists())
			{
				root.mkdirs();
			}
			
			htmlWriter = new FileWriter(html);
			
			htmlWriter.write("");
    		htmlWriter.flush();

			SetAutoRefresh(htmlWriter);
			
			htmlWriter.write("<b>Statistics</b><br>");
			htmlWriter.write("  There are " + GetNumNodes() + " peers<br>");
                        htmlWriter.write("  We used " + maxWorkers + " workers<br>");
                        htmlWriter.write("  The crawl took " + timeTaken + " seconds<br>");
                        htmlWriter.write("  That's " + (GetNumNodes() / timeTaken) + " peers per second<br><br>");
                        
                        htmlWriter.write(" Node Status Breakdown:<br>");
                        htmlWriter.write(" Connection timed out: " + CountStatus("Connection timed out") + "<br>");
                        htmlWriter.write(" Peers Crawled: " + CountStatus("Peers Crawled") + "<br>");
                        htmlWriter.write(" Gnutella Error: " + CountStatus("Gnutella Error") + "<br>");
                        htmlWriter.write(" TCP Error: " + CountStatus("TCP Error") + "<br>");
                        htmlWriter.write(" Uncrawled: " + CountStatus("Uncrawled") + "<br>");
                        
			
    		//DrawToFile(directory, "NetworkGraph");
    		//htmlWriter.write("<b>Network Graph</b><br>");
    		//htmlWriter.write("  Green nodes were UltraPeers when first seen<br>");
    		//htmlWriter.write("  Red nodes were leaves when first seen<br>");
    		//htmlWriter.write("<img src=\"NetworkGraph.png\" width = \"" + mWidth + "\" height=\"" + mHeight + "\"><br><br>");

    		htmlWriter.write("<b>Crawled Nodes</b><br>");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
    	
        System.out.println("Writing nodes to html");
    	Iterator<GraphNode> nodeIt = mNodes.iterator();
    	while(nodeIt.hasNext())
    	{
    		GnutellaNode node = (GnutellaNode)nodeIt.next();
    		CrawlResult result = node.GetLatestResult();
    		if(result == null)
    		{
    			continue;
    		}
    		
                
    		try {
    			String pageName = result.Address.replace('.', '_');
    			pageName = pageName.replace(':','_');
    			pageName = pageName + ".htm";
			
    			//htmlWriter.write("<b><a href=\""+ pageName +"\">"+ result.HostName 
    			//		+ " (" + result.Address +")</b></a>");
                        //htmlWriter.write("<b><a href=\""+ pageName +"\">"+ result.Address 
    			//		+ "</b></a>");
    			htmlWriter.write("<b>"+ result.Address 
    					+ "</b>");
    			htmlWriter.write("<br>Running Agent:        "+result.Agent);
                        htmlWriter.write("<br>Crawl Status:         "+result.Status);
    			//htmlWriter.write("<br>Number of UltraPeers: "+result.GetPeers().length);
    			//htmlWriter.write("<br>Number of Leaf Peers: "+result.GetLeaves().length);
    			//htmlWriter.write("<br>Number of Files:      "+result.GetFiles().size());
    			//htmlWriter.write("<br>Crawled at " + result.GetCrawlDate());
	    
    			//File linkedPage = new File(directory, "temp_" + pageName);
    			//FileWriter linkedHTML = new FileWriter(linkedPage);
    			//result.PrintHTMLResult(linkedHTML,pageName);
    			//linkedHTML.close();
    			
    			// rename temp_page.htm to page.htm
    			//boolean renameSuccess = false;
    			//for(int i = 0; i < 10 && !renameSuccess; ++i)
    			//{
    			//	renameSuccess = linkedPage.renameTo(new File(directory, pageName));
    			//}
    			//if(!renameSuccess)
    			//{
    			//	System.out.println("Couldn't rename the html file to " + pageName);
    			//}
    			
    			htmlWriter.write("<br><br>");
		
    			htmlWriter.flush();
                        
    			
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			return;
    		}
                        
    	}

        try {
			htmlWriter.close();
			
			// rename temp.htm to index.htm
			boolean renameSuccess = false;
			for(int i = 0; i < 10 && !renameSuccess; ++i)
			{
				renameSuccess = html.renameTo(new File(root, "index.htm"));
			}
			if(!renameSuccess)
			{
				System.out.println("Couldn't rename the html file to index.htm");
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
    }
    
    private void SetAutoRefresh(FileWriter htmlWriter) throws IOException
    {
    	// This script will setup a auto refresh every 20 seconds.
    	// We tend to crawl 3 nodes per minute, so this should work out well.
    	
    	String script = "<script>\n"
+ "<!--\n"
+ "var limit=\"0:20\"\n"

+ "if (document.images){\n"
+ "var parselimit=limit.split(\":\")\n"
+ "parselimit=parselimit[0]*60+parselimit[1]*1\n"
+ "}\n"
+ "function beginrefresh(){\n"
+ "if (!document.images)\n"
+ "return\n"
+ "if (parselimit==1)\n"
+ "window.location.reload()\n"
+ "else{ \n"
+ "parselimit-=1\n"
+ "curmin=Math.floor(parselimit/60)\n"
+ "cursec=parselimit%60\n"
+ "if (curmin!=0)\n"
+ "curtime=curmin+\" minutes and \"+cursec+\" seconds left until page refresh!\"\n"
+ "else\n"
+ "curtime=cursec+\" seconds left until page refresh!\"\n"
+ "window.status=curtime\n"
+ "setTimeout(\"beginrefresh()\",1000)\n"
+ "}\n"
+ "}\n"

+ "window.onload=beginrefresh\n"
+ "//-->\n"
+ "</script>\n";
  
    	htmlWriter.write(script);
    }
    
    public void ProcessResult(CrawlResult result)
    {
    	//code below is added in case cannot connect to IP address
    	if (result==null) return;
    	GnutellaNode crawledNode = GetNode(result.Address, true); // Todo - is this an ultrapeer?
    	crawledNode.SaveLatestResult(result);
    	
        //String[] peers = result.GetPeers();
    	//String[] leaves = result.GetLeaves();
        
    	System.out.println("Processing result from " + crawledNode.GetAddress());
        System.out.println("  Agent: " + result.Agent);
        System.out.println("  Status: " + result.Status);
    	//System.out.println("  Num UltraPeers: " + peers.length);
    	//System.out.println("  Num Leaves: " + leaves.length);
    	//System.out.println("  Num Files:" + result.GetFiles().size());
    	
    	// Add all the UltraPeer connections for this node
    	/*for(int i = 0; i < peers.length; ++i)
    	{
    		String ultraPeerAddress = peers[i];
    		
    		//System.out.println("Adding UltraPeer " + ultraPeerAddress);
    		GnutellaNode ultraPeerNode = GetNode(ultraPeerAddress, true);
    		
    		GraphLink link = new GraphLink(crawledNode, ultraPeerNode);
    		crawledNode.AddLink(link);
    		ultraPeerNode.AddLink(link);
    		AddLink(link);
    	}
    	
    	// Add all the leaf connections for this node
    	for(int i = 0; i < leaves.length; ++i)
    	{
    		String leafPeerAddress = leaves[i];
    		
    		//System.out.println("Adding LeafPeer " + leafPeerAddress);
    		GnutellaNode leafPeerNode = GetNode(leafPeerAddress, false);
    		
    		GraphLink link = new GraphLink(crawledNode, leafPeerNode);
    		crawledNode.AddLink(link);
    		leafPeerNode.AddLink(link);
    		AddLink(link);
    	}*/
    	
    	// Add the files for this peer
    	/*Iterator<String> fileIt = result.GetFiles().iterator();
    	while(fileIt.hasNext())
    	{
    		String file = fileIt.next();
    		if(!mFiles.contains(file))
    		{
    			mFiles.add(file);
    		}
    	}*/
    	
    	crawledNode.SetLastTimeCrawled();
    }
    
    public String GetAddressToCrawl()
    {
    	if(mNodes.size() < 1)
    	{
    		return null;
    	}
    	//System.out.println("Finding oldest node out of " + mNodes.size() + " nodes");
    	
    	GnutellaNode oldestNode = (GnutellaNode)mNodes.elementAt(0);
    	
    	Iterator<GraphNode> nodeIt = mNodes.iterator();
    	while(nodeIt.hasNext())
    	{
    		GnutellaNode node = (GnutellaNode)nodeIt.next();
    		//System.out.println("Checking for oldest node: " + node.GetAddress() + " updated at " + node.GetLastTimeCrawled() + " Ultrapeer=" + node.GetIsUltrapeer());
    		if(oldestNode.GetLastTimeCrawled() > node.GetLastTimeCrawled())
    		{
    			//System.out.println("Found new oldest!");
    			oldestNode = node;
    		}
    	}
    	
    	//System.out.println("Oldest node was " + oldestNode.GetAddress());
    	return oldestNode.GetAddress();
    }
    
    public GnutellaNode GetNode(String address, boolean ultrapeer)
    {
    	Iterator<GraphNode> nodeIt = mNodes.iterator();
    	while(nodeIt.hasNext())
    	{
    		GnutellaNode node = (GnutellaNode)nodeIt.next();
    		if(node.GetAddress().equals(address))
    		{
    			return node;
    		}
    	}
    	
    	GnutellaNode newNode = new GnutellaNode(address, true);//ultrapeer,);
    	mNodes.add(newNode);
    	return newNode;
    }
    
    public void NotifyCrawlFailure(String address)
    {
    	Iterator<GraphNode> nodeIt = mNodes.iterator();
    	while(nodeIt.hasNext())
    	{
    		GnutellaNode node = (GnutellaNode)nodeIt.next();
    		if(node.GetAddress().equals(address))
    		{
    			node.SetLastTimeCrawled();
    		}
    	}
    }
    
    public int GetNumUltraPeers()
    {
    	int count = 0;
    	
    	Iterator<GraphNode> nodeIt = mNodes.iterator();
    	while(nodeIt.hasNext())
    	{
    		GnutellaNode node = (GnutellaNode)nodeIt.next();
    		if(node.GetIsUltrapeer())
    		{
    			++count;
    		}
    	}
    	
    	return count;
    }
    
    public int GetNumLeaves()
    {
    	int count = 0;
    	
    	Iterator<GraphNode> nodeIt = mNodes.iterator();
    	while(nodeIt.hasNext())
    	{
    		GnutellaNode node = (GnutellaNode)nodeIt.next();
    		if(!node.GetIsUltrapeer())
    		{
    			++count;
    		}
    	}
    	
    	return count;
    }
    
    @Override
    public void OrganizeNodesInCircle()
    {
        Grid();
    }
    
    public void Grid()
    {
        int numNodes = mNodes.size();
        if(numNodes < 1)
        {
            return;
        }
        
        float steps = (float)Math.ceil(Math.sqrt((double)numNodes));
        
        float xStep = ((float)mWidth) / steps;
        float yStep = ((float)mHeight) / steps;
        float offset = (float)Math.min(xStep, yStep) / 2.0f;
        
        int numSteps = (int)steps;
        int index = 0;
        for(int y = 0; y < numSteps; ++y)
        {
            for(int x = 0; x < numSteps; ++x)
            {
                if(index >= numNodes)
                {
                    return;
                }
                
                GnutellaNode node = (GnutellaNode)mNodes.elementAt(index);
                node.SetPosition(new Point2D.Float((xStep * x) + offset, (yStep * y) + offset));
                node.SetSize(xStep / 4.0f);
                ++index;
            }
        }
        
    }
    
    public void IP_To_XY()
    {
        int numNodes = mNodes.size();
        if(numNodes < 1)
        {
        	return;
        }
        
        float xStep = ((float)mWidth) / (0xFFFF);
        float yStep = ((float)mHeight) / (0xFFFF);
        
        Iterator<GraphNode> it = mNodes.iterator();
        while(it.hasNext())
        {
            try
            {
            GnutellaNode node = (GnutellaNode)it.next();
            String temp = node.GetAddress();
            System.out.println("Parsing address: " + temp);
            temp = temp.substring(0, temp.indexOf(":"));
            String[] tokens = temp.split("\\.");
            float x = (float)((Integer.parseInt(tokens[0]) << 8) + (Integer.parseInt(tokens[1])));
            float y = (float)((Integer.parseInt(tokens[2]) << 8) + (Integer.parseInt(tokens[3])));
            node.SetPosition(new Point2D.Float(x * xStep, y * yStep));
            node.SetSize(2);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
        
        
        /*double circleDiv = (2.0 * Math.PI) / ((double)numNodes);
        Point2D.Float center = new Point2D.Float(mWidth/2, mHeight/2);
        float approxCircumf = Math.min((float)mWidth, (float)mHeight) * (float)Math.PI;
        float nodeSize = approxCircumf / (numNodes * 4);
        float distance = (Math.min((float)mWidth, (float)mHeight) - nodeSize) / 2;   
        distance -= 2.0f; // make a small border.
        
        for(int i = 0; i < numNodes; ++i)
        {
            float x = (float)Math.sin(circleDiv * (double)i) * distance;
            float y = (float)Math.cos(circleDiv * (double)i) * distance;
            mNodes.elementAt(i).SetPosition(new Point2D.Float(center.x + x, center.y + y));
            mNodes.elementAt(i).SetSize(nodeSize);
        }*/
    }
}
